链表的基本操作
2013-08-17 16:26:51
再次写链表的基本操作,包括前插法创建链表、链表的插入、删除、排序、翻转、显示、销毁。
此次写的链表时带有头指针的,是否有头指针,对于链表的各个操作都会有影响,与之前写的不带头指针的链表相比,确实方便很多,不易出错。
小结:
- 对于带有头结点的链表,空链表(pHead->next= NULL)与头指针为NULL的链表(pHead = NULL)是不同的,后者是非法的链表,要当做异常处理;
- 函数入口对于异常输入的处理,比如下面代码中多个函数中的assert(pHead != NULL);排除了链表头结点为空的异常情况;
- 函数对于特殊输入的处理,比如插入、删除的位置大于链表长度,以及链表为空的处理;链表翻转时链表长度为空或1时直接返回即可;在函数开始时最好就对这些特殊的输入进行处理,以免遗漏;注意插入、删除的是第一个以及最后一个的情况
- 存放插入、删除的位置、链表长度的变量为size_t类型的,排除了非法的负数的情况,但也要注意这种做法带来的副作用,就是对于size_t类型的变量0-1的结果为最大的正数,而非负数,判断循环结束时,应注意到这个问题。
- 输入ctrl+z结束键盘输入后,要想再次从键盘接收输入,必须用cin.clear(); cin.sync();清楚流,否则不能接收输入
下面是完整的代码(包括测试):
1 #include <iostream> 2 #include <cassert> 3 using namespace std; 4 5 typedef int DataType; //链表元素类型 6 7 typedef struct node //链表结点 8 { 9 DataType data; 10 node *next; 11 12 }LNode,*PLNode; 13 14 //创建带有头结点的链表 15 //输入ctrl+z结束 16 //有无头结点将会影响到对链表的所有操作,包括显示链表元素、插入、删除、销毁等 17 PLNode CreatLink() 18 { 19 PLNode pHead = new LNode; //注意delete 20 PLNode pPre = pHead; 21 PLNode pCur = NULL; 22 pHead->data = 0; //空头 23 pHead->next = pCur; 24 25 DataType dataTmp = 0; 26 27 cout<<"Please enter the elements of the link ,separate with space,and end with ctrl+z :"<<endl; 28 while (cin>>dataTmp) 29 { 30 pCur = new LNode; 31 pCur->data = dataTmp; 32 pCur->next = NULL; 33 34 pPre->next = pCur; 35 pPre = pCur; 36 } 37 38 return pHead; 39 } 40 41 //创建不带头结点的链表 42 //输入ctrl+z结束 43 PLNode CreatLinkWithNonNullHead() 44 { 45 PLNode pHead = NULL; //delete 46 PLNode pPre = NULL; 47 PLNode pCur = NULL; 48 49 DataType dataTmp = 0; 50 51 cout<<"Please enter the elements of the link ,separate with space,and end with ctrl+z :"<<endl; 52 while (cin>>dataTmp) 53 { 54 pCur = new LNode; 55 pCur->data = dataTmp; 56 pCur->next = NULL; 57 58 if (NULL == pHead) //对头的特别处理 59 { 60 pHead = pCur; 61 pPre = pCur; 62 } 63 else 64 { 65 pPre->next = pCur; 66 } 67 pPre = pCur; 68 } 69 return pHead; 70 } 71 72 //对头结点为pHead的链表,在位置posToInsert处插入元素dataToInsert 73 //posToInsert定义为size_t类型,排除了位置为负数的异常输入 74 //注意对输入位置超过链表长度的处理 75 PLNode InsertLink(PLNode &pHead,size_t posToInsert,const DataType dataToInsert) 76 { 77 assert(pHead != NULL); 78 79 PLNode pCur = pHead; 80 PLNode pNew = new LNode; 81 pNew->data = dataToInsert; 82 pNew->next = NULL; 83 84 while (posToInsert--) 85 { 86 //assert(pCur != NULL); //保证不超过链表长度 87 pCur = pCur->next; 88 assert(pCur != NULL); //保证不超过链表长度,放在前面当posToInsert减到0时会出错 89 } 90 91 pNew->next = pCur->next; 92 pCur->next = pNew; 93 94 return pHead; 95 } 96 97 //删除结点指针指向的元素,未测试 98 PLNode InsertLinkAtNode(PLNode &pHead,const PLNode pPosToInsert,const DataType dataToInsert) 99 { 100 assert(pHead != NULL); 101 102 PLNode pCur = pHead; 103 PLNode pNew = new LNode; 104 pNew->data = dataToInsert; 105 pNew->next = NULL; 106 107 while (pCur != pPosToInsert) 108 { 109 assert(pCur != NULL); //保证不超过链表长度 110 pCur = pCur->next; 111 } 112 113 pNew->next = pCur->next; 114 pCur->next = pNew; 115 116 return pHead; 117 } 118 119 //对头结点为pHead的链表,在位置posToInsert处插入元素dataToInsert 120 //posToInsert定义为size_t类型,排除了位置为负数的异常输入 121 //注意对输入位置超过链表长度的处理 122 PLNode DeleteLink(PLNode &pHead,size_t posToDelete) 123 { 124 assert(pHead != NULL); 125 126 if (0 == posToDelete) 127 { 128 return pHead; 129 } 130 131 PLNode pCur = pHead; 132 PLNode pNodeToDelete = NULL; 133 size_t posPriorToDelete = posToDelete - 1; 134 135 while (posPriorToDelete--) 136 { 137 pCur = pCur->next; 138 assert(pCur != NULL); //保证不超过链表长度,放在前面当posToInsert减到0时会出错 139 } 140 141 pNodeToDelete = pCur->next; 142 assert(pNodeToDelete != NULL); ////保证不超过链表长度 143 pCur->next = pNodeToDelete->next; 144 delete pNodeToDelete; 145 return pHead; 146 } 147 148 //获取链表长度 149 size_t GetLengthOfLink(PLNode pHead) 150 { 151 assert(NULL != pHead); 152 153 size_t lengthOfLink = 0; 154 PLNode pCur = pHead; 155 156 while (NULL != pCur->next) 157 { 158 ++lengthOfLink; 159 pCur = pCur->next; 160 } 161 162 return lengthOfLink; 163 } 164 165 //冒泡法最链表元素排序 166 PLNode BubbleSortLink(PLNode &pHead) 167 { 168 assert(NULL != pHead); 169 170 PLNode pCur = pHead->next; 171 172 size_t lengthOfLink = GetLengthOfLink(pHead); 173 size_t tmp = 0; 174 int i = (int)lengthOfLink; //定义为int型,在lengthOfLink为0时,lengthOfLink--为-1 175 int j = 0; 176 177 while (i-- > 1) 178 { 179 pCur = pHead->next; //每次都指向第一个结点 180 j = i; 181 /*while (j--)*/ 182 while (j-- > 0) 183 { 184 if (pCur->data > pCur->next->data) 185 { 186 tmp = pCur->data; 187 pCur->data = pCur->next->data; 188 pCur->next->data = tmp; 189 } 190 191 pCur = pCur->next; 192 } 193 } 194 return pHead; 195 } 196 197 //链表元素翻转 198 PLNode ReverseLink(PLNode &pHead) 199 { 200 assert(NULL != pHead); 201 202 if (NULL == pHead->next || NULL == pHead->next->next) //短路求值的特性保证pHead->next->next不会因为pHead->next == NULL而非法操作 203 { 204 return pHead; 205 } 206 207 PLNode pPre = pHead->next; //上面的if保证此处的访问都是合法的 208 PLNode pCur = pPre->next; 209 PLNode pNxt = NULL; 210 211 pPre->next = NULL; //此时pPre为翻转后链表的最后一个结点,将next置为NULL 212 213 while (pCur != NULL) 214 { 215 pNxt = pCur->next; 216 pCur->next = pPre; //指正方向翻转 217 218 pPre = pCur; 219 pCur = pNxt; 220 } 221 222 //pHead = pPre; 223 pHead->next = pPre; 224 return pHead; 225 } 226 227 //显示有头结点的链表的元素 228 void Displaylink(const PLNode &pHead) 229 { 230 assert(pHead != NULL); 231 PLNode pCur = pHead->next; 232 while (pCur != NULL) 233 { 234 cout<<pCur->data<<"\t"; 235 pCur = pCur->next; 236 } 237 cout<<endl; 238 } 239 240 //显示没有头结点的链表的元素 241 void DisplaylinkWithNonNullHead(const PLNode &pHead) 242 { 243 PLNode pCur = pHead; 244 while (pCur != NULL) 245 { 246 cout<<pCur->data<<"\t"; 247 pCur = pCur->next; 248 } 249 cout<<endl; 250 } 251 252 //带有头结点的链表的销毁 253 void Destorylink(PLNode &pHead) 254 { 255 assert(pHead != NULL); 256 PLNode pCur = pHead->next; 257 while (pCur != NULL) 258 { 259 delete pHead; //删除头结点 260 pHead = pCur; 261 pCur = pCur->next; 262 } 263 } 264 265 //测试链表操作 266 //除了作为比较的建立链表操作,链表的所有操作都基于带有头结点的链表 267 //因为带有头结点的链表在各种操作上都会比较方便 268 void Testlink() 269 { 270 PLNode pHead = NULL; 271 272 //Test CreatLinkWithNullHead... 273 cout<<"Test CreatLink..."<<endl; 274 pHead = CreatLink(); 275 cout<<"display the link created by CreatLink : "<<endl; 276 Displaylink(pHead); 277 278 cin.clear(); //清除流状态 279 cin.sync(); 280 281 //Test InsertLink... 282 /*size_t posToInsert = 0; 283 DataType dataToInsert = 0; 284 285 cout<<"Please enter the position and the data to insert,end with ctrl+z :"<<endl; 286 while (cin>>posToInsert>>dataToInsert) 287 { 288 cout<<"The link before insertion : "<<endl; 289 Displaylink(pHead); 290 291 pHead = InsertLink(pHead,posToInsert,dataToInsert); 292 293 cout<<"The link after insertion : "<<endl; 294 Displaylink(pHead); 295 296 cout<<"Please enter the position to insert,end with ctrl+z :"<<endl; 297 } 298 cin.clear(); //清除流状态 299 cin.sync();*/ 300 301 //Test DeleteLink... 302 //size_t posToDelete = 0; 303 // 304 //cout<<"Please enter the position to delete,end with ctrl+z :"<<endl; 305 //while (cin>>posToDelete) 306 //{ 307 // cout<<"The link before insertion : "<<endl; 308 // Displaylink(pHead); 309 310 // pHead = DeleteLink(pHead,posToDelete); 311 312 // cout<<"The link after delete : "<<endl; 313 // Displaylink(pHead); 314 315 // cout<<"Please enter the position to delete,end with ctrl+z :"<<endl; 316 //} 317 318 //cin.clear(); //清除流状态 319 //cin.sync(); 320 321 //Test GetLengthOfLink... 322 323 //size_t lengthOfLink = GetLengthOfLink(pHead); 324 cout<<"Test GetLengthOfLink..."<<endl; 325 cout<<"the length of link is : "<<GetLengthOfLink(pHead)<<endl; 326 327 //Test BubbleSortLink... 328 cout<<"Test BubbleSortLink..."<<endl; 329 BubbleSortLink(pHead); 330 cout<<"The link after sort : "<<endl; 331 Displaylink(pHead); 332 333 //Test BubbleSortLink... 334 cout<<"Test ReverseLink..."<<endl; 335 ReverseLink(pHead); 336 cout<<"The link after reverse : "<<endl; 337 Displaylink(pHead); 338 339 Destorylink(pHead); 340 341 /*cout<<"Test CreatLinkWithNonNullHead..."<<endl; 342 pHead = CreatLinkWithNonNullHead(); 343 cout<<"display the link created by CreatLinkWithNullHead : "<<endl; 344 DisplaylinkWithNonNullHead(pHead); 345 Destorylink(pHead);*/ 346 347 } 348 349 //main 350 int main() 351 { 352 Testlink(); 353 return 0; 354 }